﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint;
using System.Xml;

namespace SharePointOfView.ExtensionMethods
{
    public static class List
    {
        private static Random rnd = new Random(DateTime.Now.Millisecond);

        /// <summary>
        /// Adds Item to the selected List, will use the metadata to insert data into Columns
        /// Will throw an SPException if an error occurs
        /// SPWeb.ProcessBatchData is used to execute the command
        /// cf : http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.spweb.processbatchdata.aspx
        /// </summary>
        /// <param name="list">The list.</param>
        /// <param name="nbItems">The number of items to insert in the list</param>
        /// <param name="metadata">The metadata used to insert data into Items.</param>
        /// <param name="forceUpdate">if set to <c>true</c> force the list update.</param>
        public static void SovAddItems(this SPList list, int nbItems, Dictionary<string, string> metadata, bool forceUpdate)
        {
            if (metadata == null)
                throw new SPException(String.Format("SharePointOfView.ExtensionMethods > {0} : {1}", "AddItems", "Metadata can't be null"));

            // Adding 0 items...
            if (nbItems == 0)
                return;

            StringBuilder sbAdd = new StringBuilder(100 * nbItems);
            sbAdd.Append("<?xml version=\"1.0\" encoding=\"UTF-8\"?><Batch OnError=\"Return\">");
            Guid listGuid = list.ID;
            for (int i = 0; i < nbItems; i++)
            {
                sbAdd.AppendFormat("<Method ID=\"{0},Save\"><SetList>{1}</SetList><SetVar Name=\"ID\">New</SetVar><SetVar Name=\"Cmd\">Save</SetVar>", i, listGuid);
                foreach (KeyValuePair<string, string> pair in metadata)
                {
                    string value = pair.Value;
                    if (value == null)
                        value = rnd.Next().ToString();
                    sbAdd.AppendFormat("<SetVar Name=\"urn:schemas-microsoft-com:office:office#{0}\">{1}</SetVar>", pair.Key, value);
                }
                sbAdd.Append("</Method>");
            }
            sbAdd.Append("</Batch>");

            SPWeb parentWeb = list.ParentWeb;
            string batchResult = parentWeb.ProcessBatchData(sbAdd.ToString());
            // http://blogs.msdn.com/rogerla/archive/2008/02/12/sharepoint-2007-and-wss-3-0-dispose-patterns-by-example.aspx
            parentWeb.Dispose();

            if (forceUpdate)
                list.Update();

            string errorMessage = GetBatchCommandResult(batchResult);
            if (errorMessage != String.Empty)
                throw new SPException(String.Format("SharePointOfView.ExtensionMethods > {0} : {1}", "AddItems", errorMessage));
        }

        /// <summary>
        /// Delete all the items specified via the metadata dictionnary (ID, FileUrl), FileUrl has to be null for SPList
        /// If metadata is null, it will delete every items in the list
        /// SPWeb.ProcessBatchData is used to execute the command
        /// cf : http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.spweb.processbatchdata.aspx
        /// </summary>
        /// <param name="list">The List</param>
        /// <param name="forceUpdate">Will force the list update</param>
        /// <param name="metadata">Dictionnary with Key = Id and Value = Fileurl (if needed)</param>
        /// <returns></returns>
        public static void SovDeleteItems(this SPList list, bool forceUpdate, Dictionary<string, string> metadata)
        {
            bool isDocumentLibrary = list is SPDocumentLibrary;

            StringBuilder sbDelete = new StringBuilder(170 * list.ItemCount);
            sbDelete.Append("<?xml version=\"1.0\" encoding=\"UTF-8\"?><Batch OnError='Return'>");
            StringBuilder cmd = new StringBuilder(200);
            // get the ListGuid, format to receive the Item ID and set the CMD to delete.
            cmd.AppendFormat("<Method><SetList Scope=\"Request\">{0}</SetList><SetVar Name=\"ID\">{{0}}</SetVar><SetVar Name=\"Cmd\">Delete</SetVar>", list.ID);

            // Cmd Delete needs FileRef when it's a DocumentLibrary 
            // http://www.sharepointblogs.com/smc750/archive/2008/04/03/spweb-processbatchdata-a-list-is-a-list-is-a-list.aspx
            if (isDocumentLibrary)
                cmd.Append("<SetVar Name=\"owsfileref\">{1}</SetVar>");
            cmd.Append("</Method>");

            //Only items defined in the dictionnary will be deleted
            if (metadata != null)
                foreach (KeyValuePair<string, string> pair in metadata)
                {
                    string itemId = pair.Key;
                    if (isDocumentLibrary)
                        sbDelete.Append(string.Format(cmd.ToString(), itemId, pair.Value));
                    else
                        sbDelete.Append(string.Format(cmd.ToString(), itemId));
                }
            else
            {
                foreach (SPListItem item in list.Items)
                {
                    string itemId = item.ID.ToString();
                    if (isDocumentLibrary)
                        sbDelete.Append(string.Format(cmd.ToString(), itemId, item.File.Url));
                    else
                        sbDelete.Append(string.Format(cmd.ToString(), itemId));
                }
                foreach (SPListItem item in list.Folders)
                {
                    string itemId = item.ID.ToString();
                    sbDelete.Append(string.Format(cmd.ToString(), itemId));
                }
            }

            sbDelete.Append("</Batch>");

            SPWeb parentWeb = list.ParentWeb;
            string batchResult = parentWeb.ProcessBatchData(sbDelete.ToString());
            // http://blogs.msdn.com/rogerla/archive/2008/02/12/sharepoint-2007-and-wss-3-0-dispose-patterns-by-example.aspx
            parentWeb.Dispose();

            if (forceUpdate)
                list.Update();

            string errorMessage = GetBatchCommandResult(batchResult);
            if (errorMessage != String.Empty)
                throw new SPException(String.Format("SharePointOfView.ExtensionMethods > {0} : {1}", "DeleteAllItems", errorMessage));
        }

        #region Utilities
        /// <summary>
        /// Gets the batch command result.
        /// </summary>
        /// <param name="batchResult">The batch result.</param>
        /// <returns></returns>
        private static string GetBatchCommandResult(string batchResult)
        {
            string commandResult = String.Empty;

            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.LoadXml(batchResult);
            XmlNodeList xmlErrNodes = xmlDoc.SelectNodes("/Results/Result/ErrorText");
            int nbErrors = xmlErrNodes.Count;

            if (nbErrors > 0)
                commandResult = xmlErrNodes[0].InnerText;

            return commandResult;
        }
        #endregion
    }
}
